/*
 * Copyright (c) 2014 pkzd/1.0, 颜文泽(robin)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     1 Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     2 Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include	<idt.h>
#include	<pic.h>
#include	<ide.h>
#include	<asm.h>
#include	<sig.h>
#include	<tty.h>
#include	<map.h>
#include	<page.h>
#include	<proc.h>
#include	<param.h>

__attribute__((__aligned__(8)))
struct idt idt[IDTNUM];

trapfun	traptbl[IDTNUM] = {
	[0] = _pic0, [1 ... 3] = NULL, [4] = _pic4, [5 ... 12] = _pic15, [13] = _pic13, [14] = _pic14, 
	[15 ... 31] = _pic15, [32] = _pic32, [33] = _pic33, [34 ... 45] = NULL, 
	[46] = _pic46, [47 ... 127] = NULL, [128] = _pic80, [129 ... 255] = NULL,
};

void
pic0(uint errcode)
{
	psignal(up, SIGFPE);  
}

void
pic4(uint errcode)
{
	psignal(up, SIGFPE);
}

void
pic13(uint errcode)
{
	spin("General protection fault\n");
}

void
pic14(uint errorcode)
{
	uint cr2;
	uint n, *p;

	cr2 = rcr2();
	if(cr2 > up->p_brk && cr2 < up->p_send)
		up->p_send = cr2 & PAGESIZ_MASK;	
	n = up->p_pgdir[PG_PDE(cr2)];
	if(!(n & PG_P)){
		if(!(n = malloc0(kernmap, 1)))
			return;
		up->p_pgdir[PG_PDE(cr2)] = n | PG_P | PG_W | PG_U;	
	}
	p = (uint *)(up->p_pgdir[PG_PDE(cr2)] & PG_ADDR_MASK) + PG_PTE(cr2);
	n = *p;
	cr2 &= PAGESIZ_MASK;
	if(n & (PG_COW | PG_SWP)){
		swapin(cr2, p);
		return;
	}else{
		if(!(n & PG_P)){
			if(!(n = malloc(coremap, 1)))
				return;
			*p = n | PG_P | PG_W | PG_U;
			page_list_alloc(n);
			flush_tbl();
			memset(cr2, 0, PAGESIZ);
			return;
		}	
	}
out:
	psignal(up, SIGSEGV);
	return;
}

void
pic15(uint errorcode)
{
	psignal(up, SIGBUS);
}

void
pic32(uint errorcode)
{
	clkint();
	outb(PIC_EOI, PIC1_CMD);
	sti();
	if(highpri)
		swtch();
}

void
pic33(uint errorcode)
{
	cnsint();
	outb(PIC_EOI, PIC1_CMD);
}

void
pic46(uint errorcode)
{
	ideint();
	outb(PIC_EOI, PIC2_CMD);
	outb(PIC_EOI, PIC1_CMD);
}

void
setidt0(struct idt *ip)
{
	ip->i_off0 = 0;
	ip->i_off0 = KERN_CSEL;
	ip->i_type = 0;
	ip->i_off1 = 0;
}

void
setidt(struct idt *ip, uint addr)
{
	ip->i_off0 = (ushort)addr;
	ip->i_sel = KERN_CSEL;
	ip->i_type = 0x8e00;
	ip->i_off1 = (ushort)(addr >> 16);
}

void
setidt1(struct idt *ip, uint addr)
{
	ip->i_off0 = (ushort)addr;
	ip->i_sel = KERN_CSEL;
	ip->i_type = 0xef00;
	ip->i_off1 = (ushort)(addr >> 16);
}

void
idt_init(void)
{
	uint i;

	for(i = 0; i < IDTNUM; i++)
		traptbl[i] ? setidt(&idt[i], (uint)traptbl[i]) : setidt0(&idt[i]);
	setidt1(&idt[128], (uint)_pic80);
	lidt((uint)idt, sizeof(idt));
}
